﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DocumentsTagger.DM;
using System.ComponentModel;
using DocumentsTagger.BL;
using DocumentsTagger.DAL;
using System.Threading;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using DocumentsTagger.UI.Commands;
using System.Windows.Threading;
using DocumentsTagger.UI.Model;

namespace DocumentsTagger.UI.ViewModel
{
    /// <summary>
    /// This is the ViewModel of the application (the master part in the master-details View Models).
    /// It is hosting  within it the DocumentViewModel.
    /// Holds a list of currently available folders and documents.
    /// </summary>
    public class MainWindowViewModel : DependencyObject
    {

        #region members
        private readonly Dispatcher r_CurrentDispatcher;
        private readonly Dictionary<string, FolderUIModel> r_Folders = new Dictionary<string, FolderUIModel>();
        private static bool m_UpdateUI = true;
        private readonly TaggerBL r_TaggerBL = new TaggerBL(TaggerXMLDal.GetInstance());
        private Thread m_UpdatingThread = null;
        #endregion

        #region properties
        /// <summary>
        /// gets or sets the need to update the UI
        /// </summary>
        public bool UpdateUI
        {
            get { return m_UpdateUI; }
            set { m_UpdateUI = value; }
        }

        /// <summary>
        /// gets or sets the the available folder UI parts
        /// </summary>
        public ObservableCollection<FolderUIModel> AvailableFolders { get; set; }

        /// <summary>
        /// gets or sets the clear DB command
        /// </summary>
        public ICommand ClearDB { get; set; }

        /// <summary>
        /// gets or sets the start scanner command
        /// </summary>
        public ICommand StartScannerCommand { get; set; }

        /// <summary>
        /// get or sets the selected document details view model
        /// </summary>
        public DocumentViewModel SelectedDocumentViewModel
        {
            get { return (DocumentViewModel)GetValue(SelectedDocumentViewModelProperty); }
            set { SetValue(SelectedDocumentViewModelProperty, value); }
        }

        /// <summary>
        ///  get or sets the currently selected Document UI part.
        ///  updates the detailed VM if needed
        /// </summary>
        public DocumentUIModel SelectedDocument
        {
            get { return (DocumentUIModel)GetValue(SelectedDocumentProperty); }
            set
            {
                if (SelectedDocumentViewModel != null)
                {
                    SelectedDocumentViewModel.Dispose();
                    SelectedDocumentViewModel = null;
                }
                SetValue(SelectedDocumentProperty, value);
                SelectedDocumentViewModel = new DocumentViewModel(((DocumentUIModel)(GetValue(SelectedDocumentProperty))).Document);

                CommandManager.InvalidateRequerySuggested();
            }
        }

        #region DependencyProperties
        // Using a DependencyProperty as the backing store for LastSymbol.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedDocumentViewModelProperty =
            DependencyProperty.Register("SelectedDocumentViewModel", typeof(DocumentViewModel), typeof(MainWindowViewModel));

        // Using a DependencyProperty as the backing store for LastSymbol.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedDocumentProperty =
            DependencyProperty.Register("SelectedDocument", typeof(DocumentUIModel), typeof(MainWindowViewModel));
        #endregion
        #endregion

        #region ctor
        /// <summary>
        /// creates a new master VM
        /// </summary>
        public MainWindowViewModel()
        {
            AvailableFolders = new ObservableCollection<FolderUIModel>();
            r_CurrentDispatcher = Dispatcher.CurrentDispatcher;
            this.ClearDB = new ClearDBCommand(this);
            this.StartScannerCommand = new StartScannerCommand(this);

            StartUpdateingThread();
        }
        #endregion

        #region methods
        
        /// <summary>
        /// entry point for starting the scanning process
        /// </summary>
        public void StartScanner()
        {
            r_TaggerBL.StartScanning();
        }


        /// <summary>
        /// entry point for stopping the scanning process
        /// </summary>
        public void StopScanner()
        {
            r_TaggerBL.StopScanning();
        }

        /// <summary>
        /// handles the UI request to clear the DB
        /// </summary>
        public void Clear_DB()
        {
            // clean the Folder's UI documens
            foreach (var folder in r_Folders.Values)
            {
                folder.Documents.Clear();
            }
            // then clear the FoldersUI
            r_Folders.Clear();
            AvailableFolders.Clear();
            r_TaggerBL.CreateNewDB();
        }

        /// <summary>
        /// handle the closing of the app (stop the scanning mechanizm and stop the VM updating thread.
        /// </summary>
        public void CloseApp()
        {
            m_UpdateUI = false;
            StopScanner();
            StopUpdateingThread();
        }

        /// <summary>
        /// this function start the VM updating thread.
        /// </summary>
        private void StartUpdateingThread()
        {
            m_UpdatingThread = new Thread(new ThreadStart(UpdateVM));
            m_UpdatingThread.Name = "UpdateVMThread";
            m_UpdatingThread.Start();
        }

        /// <summary>
        /// this function aborts the VM updating thread
        /// </summary>
        private void StopUpdateingThread()
        {
            if (m_UpdatingThread != null)
            {
                m_UpdatingThread.Abort();
            }
        }

        /// <summary>
        /// the base function for the VM updateing thread
        /// </summary>
        private void UpdateVM()
        {
            while (m_UpdateUI)
            {
                UpdateDocuments();

                Thread.Sleep(1000);
            }
        }

        /// <summary>
        /// this function makes  sure that the documents pane is in sync with the data it represents
        /// </summary>
        private void UpdateDocuments()
        {
            List<Document> DocumentsList = r_TaggerBL.GetDocuments();

            foreach (Document doc in DocumentsList)
            {
                FolderUIModel folderVM = null;
                string folderPath = doc.FilePath;
                if (!r_Folders.ContainsKey(folderPath))
                {
                    // add folder
                    Action dispatcherAction = () =>
                    {
                        folderVM = new FolderUIModel(folderPath);
                        r_Folders.Add(folderPath, folderVM);
                        AvailableFolders.Add(folderVM);
                    };
                    DispatchAction(dispatcherAction);

                }
                else
                {
                    folderVM = r_Folders[folderPath];
                }

                // add document - if needed
                if (!folderVM.HasDocument(doc.FullPath))
                {
                    Action dispatcherAction = () =>
                    {
                        folderVM.AddDocument(doc);
                    };
                    DispatchAction(dispatcherAction);

                }

                //update each document's tags
                synchronizeTags(doc);
            }
        }

        /// <summary>
        /// Askes the buisness logic to update the document's tags
        /// </summary>
        /// <param name="doc">The document to update</param>
        private void synchronizeTags(Document doc)
        {
            r_TaggerBL.UpdateDocumentTags(doc);
        }

        /// <summary>
        /// a handy function to help returning to the right UI thread.
        /// </summary>
        /// <param name="action">the action that needs to be preformed on the IU thread</param>
        private void DispatchAction(Action action)
        {
            r_CurrentDispatcher.Invoke(DispatcherPriority.Normal, action);
        }


        /// <summary>
        /// handles UI request to remove a document from documents selection pane
        /// </summary>
        /// <param name="document">the document to remove</param>
        internal void RemoveDocument(Document document)
        {
            r_TaggerBL.RemoveDocument(document);
            r_Folders[document.FilePath].RemoveDocument(document.FullPath);
        }
        #endregion

        
    }
}
